<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <title>Kexio 模块3-认证授权模块 · 流程图</title>
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <style>
    :root { --bg:#f7f9fc; --fg:#0f172a; --muted:#64748b; --accent:#2563eb; --ok:#16a34a; --warn:#ea580c; --err:#dc2626; --box:#ffffff; --line:#cbd5e1; }
    body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Inter,Arial;background:var(--bg);color:var(--fg)}
    .wrap{max-width:1100px;margin:24px auto;padding:0 16px}
    h1{font-size:20px;margin:0 0 16px}
    .board{display:grid;grid-template-columns:repeat(3,minmax(300px,1fr));gap:16px;align-items:start}
    @media (max-width:1024px){.board{grid-template-columns:repeat(1,minmax(300px,1fr))}}
    .lane-title{font-size:13px;color:var(--muted);padding:0 4px 4px}
    .col{display:flex;flex-direction:column;gap:10px}
    .box{background:var(--box);border:1px solid var(--line);border-radius:8px;padding:10px 12px;box-shadow:0 1px 2px rgba(0,0,0,.04);font-size:13px}
    .box .hd{font-weight:600;margin-bottom:6px}
    .box .cls{color:var(--accent);font-weight:600}
    .box .tx{color:var(--muted);line-height:1.35}
    .ok{border-left:4px solid var(--ok)}.warn{border-left:4px solid var(--warn)}.err{border-left:4px solid var(--err)}
    .arrow-v{display:flex;align-items:center;justify-content:center;height:18px}
    .arrow-v::after{content:"↓";color:var(--muted);font-size:18px}
    code{background:#eef2ff;color:#1e293b;padding:1px 6px;border-radius:6px}
    .legend{margin-top:18px;display:flex;gap:12px;flex-wrap:wrap;font-size:12px;color:var(--muted)}
    .pill{padding:2px 8px;border-radius:999px;border:1px solid var(--line);background:#fff}
  </style>
</head>
<body>
<div class="wrap">
  <div class="nav"><a href="权限总览.html">总览</a> <a href="模块1-应用启动模块.html">模块1</a> <a href="模块2-通用工具模块.html">模块2</a> <a class="active" href="模块3-认证授权模块.html">模块3</a> <a href="模块4-用户管理模块.html">模块4</a></div>
  <h1>模块3 · 认证授权模块（登录 / 登出 / 受保护接口）</h1>
  <style>
    .nav{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 12px}
    .nav a{padding:4px 10px;border:1px solid var(--line);border-radius:999px;background:#fff;color:var(--accent);text-decoration:none}
    .nav a.active{background:#eef2ff}
  </style>

  <div class="board">
    <!-- 登录流程 -->
    <div>
      <div class="lane-title">登录流程</div>
      <div class="col">
        <div class="box ok"><div class="hd">1) 提交账号密码</div><div class="tx">POST <code>/api/auth/login</code></div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">2) 控制器接收</div><div class="tx"><span class="cls">com.kexio.user.controller.AuthController</span></div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">3) 服务层认证</div><div class="tx"><span class="cls">com.kexio.user.service.impl.AuthServiceImpl</span><br/>- 校验/租户处理/日志/缓存</div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">4) 颁发Token</div><div class="tx"><span class="cls">com.kexio.auth.security.JwtProvider</span></div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">5) 预热只读画像</div><div class="tx"><span class="cls">PermissionProjectionWriter</span> → <span class="cls">PermissionVersionService</span> → <span class="cls">PermissionChangeNotifier</span></div></div>
      </div>
    </div>

    <!-- 登出流程 -->
    <div>
      <div class="lane-title">登出流程</div>
      <div class="col">
        <div class="box warn"><div class="hd">1) 发起登出</div><div class="tx">POST <code>/api/auth/logout</code></div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">2) 令牌回收/会话终止</div><div class="tx"><span class="cls">TokenBlacklistService</span> / <span class="cls">SessionService</span></div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">3) 版本/缓存失效</div><div class="tx"><span class="cls">PermissionVersionService</span> / <span class="cls">CacheService</span> / 事件通知</div></div>
      </div>
    </div>

    <!-- 受保护接口鉴权流程 -->
    <div>
      <div class="lane-title">受保护接口鉴权</div>
      <div class="col">
        <div class="box ok"><div class="hd">1) 白名单判定</div><div class="tx"><span class="cls">WebSecurityConfig</span> + <span class="cls">PublicApiSecurityConfigurer</span></div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">2) JWT过滤</div><div class="tx"><span class="cls">JwtAuthenticationFilter</span> → <span class="cls">JwtProvider</span></div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">3) 用户画像装配</div><div class="tx"><span class="cls">MultiLevelCacheService</span> / <span class="cls">UserAuthInfoProvider</span></div></div>
        <div class="arrow-v"></div>
        <div class="box"><div class="hd">4) 方法权限</div><div class="tx"><span class="cls">PermissionAspect</span> → <span class="cls">PermissionEvaluator</span>（可选 <span class="cls">DataScopeAspect</span>）</div></div>
      </div>
    </div>
  </div>

  <div class="legend">
    <span class="pill">读侧：<code>RedisUserAuthInfoProvider</code>（redis-projection.enabled=true）</span>
    <span class="pill">事件：<code>RedisStreamsPermissionChangeNotifier/Subscriber</code>（publisher=redis）</span>
    <span class="pill">版本：<code>PermissionVersionService</code></span>
  </div>
</div>
</body>
</html>

